﻿namespace Sustainalytics.ClientDW.Synchronization
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;

    using Amazon;
    using Amazon.EC2;
    using Amazon.EC2.Model;

    public interface IPDFMachineManagement
    {
        Task<MachineState> GetAWSInstanceStateAsync();

        Task<bool> PollForMachineStateAsync();

        Task<bool> StartAWSInstanceAsync();

        Task<bool> StopAWSIntanceAsync();
    }

    public class PDFMachineManagement : IPDFMachineManagement
    {
        private static readonly Dictionary<string, MachineState> AmazonStateToMachineState =
            new Dictionary<string, MachineState>
                {
                    { "pending", MachineState.Pending }, 
                    { "running", MachineState.Running }, 
                    { "shutting-down", MachineState.ShuttingDown }, 
                    { "terminated", MachineState.Terminated }, 
                    { "rebooting", MachineState.Restarted }, 
                    { "stopping", MachineState.Stopping }, 
                    { "stopped", MachineState.Stopped }
                };

        private readonly string _accesKeyId;
        private readonly string _instance;
        private readonly RegionEndpoint _region;
        private readonly string _secretKey;

        public PDFMachineManagement(string instance, string accesKeyId, string secretKey, RegionEndpoint region)
        {
            this._instance = instance;
            this._accesKeyId = accesKeyId;
            this._secretKey = secretKey;
            this._region = region;
        }

        public async Task<MachineState> GetAWSInstanceStateAsync()
        {
            using (var client = new AmazonEC2Client(_accesKeyId, _secretKey, _region))
            {
                var request = new DescribeInstanceStatusRequest();
                var response = await client.DescribeInstanceStatusAsync(request).ConfigureAwait(false);
                var machineStatus =
                    response.InstanceStatuses.FirstOrDefault(
                        i => string.Equals(i.InstanceId, _instance, StringComparison.InvariantCultureIgnoreCase));
                return machineStatus == null
                           ? MachineState.Stopped
                           : AmazonStateToMachineState[machineStatus.InstanceState.Name];
            }
        }

        public async Task<bool> PollForMachineStateAsync()
        {
            Func<Task<bool>> verifiable =
                async () => await this.GetAWSInstanceStateAsync().ConfigureAwait(false) != MachineState.Running;

            return await RetryPolicy.DoAsync(verifiable).ConfigureAwait(false);
        }

        public async Task<bool> StartAWSInstanceAsync()
        {
            if (await this.GetAWSInstanceStateAsync().ConfigureAwait(false) == MachineState.Running)
            {
                return true;
            }

            using (var client = new AmazonEC2Client(_accesKeyId, _secretKey, _region))
            {
                var req = new StartInstancesRequest(new List<string> { _instance });
                var response = await client.StartInstancesAsync(req).ConfigureAwait(false);

                var createdInstance = response.StartingInstances.FirstOrDefault(x => x.InstanceId == _instance);

                if (createdInstance != null)
                {
                    return AmazonStateToMachineState[createdInstance.CurrentState.Name] == MachineState.Pending;
                }
            }

            return false;
        }

        public async Task<bool> StopAWSIntanceAsync()
        {
            using (var client = new AmazonEC2Client(_accesKeyId, _secretKey, _region))
            {
                var request = new StopInstancesRequest { InstanceIds = new List<string> { this._instance } };

                var response = await client.StopInstancesAsync(request).ConfigureAwait(false);
                return
                    response.StoppingInstances.All(
                        c =>
                        AmazonStateToMachineState[c.CurrentState.Name.Value] == MachineState.Stopping
                        || AmazonStateToMachineState[c.CurrentState.Name.Value] == MachineState.Stopped);
            }
        }
    }
}